#include <iostream>
#include <vector>
#include "llvm/IR/Module.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/GlobalVariable.h"

#define println(x) std::cout << (x) << std::endl 

using namespace llvm;

void codegen_function(){
    LLVMContext context;

    Module* module = new Module("helloModule", context);

    Type* voidType = Type::getVoidTy(context);
    FunctionType* functionType = FunctionType::get(voidType, false);
    Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "HelloFunction", module);

    // 检查是否符合规范
    verifyFunction(*function);
    module->print(outs(), nullptr);
}

void test_plc() {
    LLVMContext context;
    IRBuilder<> builder(context);
    Module* module = new Module("test.plc", context);

    // codegen for PLC_PRG
    auto functionType = FunctionType::get(builder.getVoidTy(), false);
    auto plc_prg = Function::Create(functionType, GlobalValue::ExternalLinkage, "PLC_PRG", module);

    auto entry = BasicBlock::Create(context, "entry", plc_prg);
    auto for_cond = BasicBlock::Create(context, "for.cond", plc_prg);
    auto for_body = BasicBlock::Create(context, "for.body", plc_prg);
    auto for_inc = BasicBlock::Create(context, "for.inc", plc_prg);
    auto for_end = BasicBlock::Create(context, "for.end", plc_prg);

    // fill entry block
    builder.SetInsertPoint(entry);
    auto counter_ptr = builder.CreateAlloca(builder.getInt32Ty(), nullptr, "COUNTER");
    auto varl_ptr = builder.CreateAlloca(builder.getInt32Ty(), nullptr, "VARL");
    builder.CreateStore(builder.getInt32(0), counter_ptr);
    builder.CreateStore(builder.getInt32(0), varl_ptr);
    builder.CreateStore(builder.getInt32(1), counter_ptr);
    builder.CreateBr(for_cond);

    // fill for cond block
    builder.SetInsertPoint(for_cond);
    auto counter_tmp1 = builder.CreateLoad(builder.getInt32Ty(), counter_ptr);
    auto cmp = builder.CreateICmpSLE(counter_tmp1, builder.getInt32(5), "cmp");
    builder.CreateCondBr(cmp, for_body, for_end);

    // fill for body block
    builder.SetInsertPoint(for_body);
    auto varl_tmp1 = builder.CreateLoad(builder.getInt32Ty(), varl_ptr);
    auto add = builder.CreateNSWAdd(varl_tmp1, builder.getInt32(3));
    builder.CreateStore(add, varl_ptr);
    builder.CreateBr(for_inc);

    // fill for inc block
    builder.SetInsertPoint(for_inc);
    auto count_tmp2 = builder.CreateLoad(builder.getInt32Ty(), counter_ptr);
    auto inc = builder.CreateNSWAdd(count_tmp2, builder.getInt32(1), "inc");
    builder.CreateStore(inc, counter_ptr);
    builder.CreateBr(for_cond);

    // fill for end block
    builder.SetInsertPoint(for_end);
    builder.CreateRetVoid();

    verifyFunction(*plc_prg);
    module->dump();
}

int main(){
    test_plc();
    return 0;
}